package com.pp.ent.core.manage;

import android.content.Context;
import android.os.Looper;
import android.util.Log;

import com.pp.ent.base.common.SafeHandlerDispatch;
import com.pp.ent.base.concurrent.XLCommand;
import com.pp.ent.base.log.MLog;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 管理core对象的类。外部应该使用此类的接口来获取某个core对象
 * 它使用CoreFactory来声成core对象实例。
 * 上层未注册core实现类话，注册一个默认实现（调用init函数）
 * 此类不是线程安全的，应该只在主线程调用
 * Author:dengqu
 * Date:2016/7/26
 */
public class CoreManager {
    public static final String TAG = CoreManager.class.getSimpleName();
    public static final String TAG_EVENT = "CoreManager_Event";

    //通过key把所有这样的key的对象方法保存起来
    private static Map<String, Map<Object, Method>> keyUIMethods = new HashMap<>();
    //通过key把所有这样的key的对象方法保存起来
    private static Map<String, Map<Object, Method>> keyServiceMethods = new HashMap<>();
    private static SafeHandlerDispatch safeHandler = new SafeHandlerDispatch(Looper.getMainLooper());

    private static Context context;

    public static Context getContext() {
        return context;
    }

    /**
     * 把对象中含有UIHandler和ServiceHandler注解的类统一保存起来
     *
     * @param client
     */
    public static void addClient(Object client) {
        if (client == null) {
            Log.w(TAG_EVENT, "Don't give me a null client");
            return;
        }

        Class<?> originalClass = client.getClass();
        if (originalClass == null) {
            Log.w(TAG_EVENT, "Client.getClass() is null");
            return;
        }
        Method[] methods = originalClass.getDeclaredMethods();

        for (Method method : methods) {
            UIHandler uiHandler = method.getAnnotation(UIHandler.class);
            if (uiHandler != null) {
                String value = uiHandler.value();
                if (value != null) {
                    addUIMethos(client, value, method);
                }
            }

            ServiceHandler serviceHandler = method.getAnnotation(ServiceHandler.class);
            if (serviceHandler != null) {
                String value = serviceHandler.value();
                if (value != null) {
                    addServiceMethos(client, value, method);
                }
            }

        }
    }


    /**
     * 通过key，把对象跟方法保存到集合中
     *
     * @param object
     * @param key
     */
    private static void addUIMethos(Object object, String key, Method method) {
        Map<Object, Method> mapObjectMethods = keyUIMethods.get(key);
        if (mapObjectMethods == null) {
            mapObjectMethods = new HashMap<>();
            keyUIMethods.put(key, mapObjectMethods);
        }
        mapObjectMethods.put(object, method);
    }

    /**
     * 通过key，把对象跟方法保存到集合中
     *
     * @param object
     * @param key
     */
    private static void addServiceMethos(Object object, String key, Method method) {
        Map<Object, Method> mapObjectMethods = keyServiceMethods.get(key);
        if (mapObjectMethods == null) {
            mapObjectMethods = new HashMap<>();
            keyServiceMethods.put(key, mapObjectMethods);
        }
        mapObjectMethods.put(object, method);
    }


    /**
     * 解绑，防止内存泄漏
     *
     * @param client
     */
    public static void remove(Object client) {
        for (Map<Object, Method> value : keyUIMethods.values()) {
            value.remove(client);
        }
        for (Map<Object, Method> value : keyServiceMethods.values()) {
            value.remove(client);
        }

    }


    static class NotifyClientRunable implements Runnable {
        private String key;
        private Object[] args;

        public NotifyClientRunable(String key, Object[] args) {
            this.key = key;
            this.args = args;
        }

        public void run() {
            notifyClients(key, args);
        }
    }


    static class NotifyCommandRunable implements Runnable {
        private XLCommand mXlCommand;

        public NotifyCommandRunable(XLCommand xlCommand) {
            mXlCommand = xlCommand;
        }

        public void run() {
            if (mXlCommand != null) {
                mXlCommand.onResponse();
            }
        }
    }

    /**
     * 通知ui的方法
     *
     * @param key
     * @param args
     */
    public static void noifyUI(String key, Object... args) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            notifyClients(key, args);
        } else {

            safeHandler.post(new NotifyClientRunable(key, args));
        }

    }

    /**
     * 异步线程命令回到主线程
     *
     * @param xlCommand
     */

    public static void noifyUI(XLCommand xlCommand) {
        safeHandler.post(new NotifyCommandRunable(xlCommand));
    }

    /**
     * 通知ui的方法
     *
     * @param key
     * @param args
     */
    public static void notifyClients(String key, Object... args) {
        Map<Object, Method> mapObjectMethods = keyUIMethods.get(key);
        if (mapObjectMethods == null || mapObjectMethods.size() == 0) {
            return;
        }
        Iterator<Map.Entry<Object, Method>> entries = mapObjectMethods.entrySet().iterator();
        while (entries.hasNext()) {
            Map.Entry<Object, Method> entry = entries.next();
            notify(entry.getKey(), entry.getValue(), args);
        }
    }

    /**
     * 通知服务类的方法
     *
     * @param key
     * @param args
     */
    public static void noifyService(String key, Object... args) {
        Map<Object, Method> mapObjectMethods = keyServiceMethods.get(key);
        if (mapObjectMethods == null || mapObjectMethods.size() == 0) {
            return;
        }
        Iterator<Map.Entry<Object, Method>> entries = mapObjectMethods.entrySet().iterator();
        while (entries.hasNext()) {
            Map.Entry<Object, Method> entry = entries.next();
            notify(entry.getKey(), entry.getValue(), args);
        }
    }


    /**
     * 通过反射执行自动执行类的特定方法
     *
     * @param object
     * @param method
     * @param args
     */
    private static void notify(Object object, Method method, Object... args) {

        try {
            if (method == null) {
                return;
            } else if (method.getParameterTypes() == null) {
                Log.e(TAG_EVENT, "Can't find " + object + " has method param:" + method.getParameterTypes() +
                        " for args[" + args.length + "]: " + args.toString());
                return;
            } else if (method.getParameterTypes().length != args.length) {
                Log.e(TAG_EVENT, "Can't find " + object + " has Method " + method.getName() +
                        " param number not matched: method(" + method.getParameterTypes().length +
                        "), args(" + args.length + ")");
                return;
            }
            MLog.debug(TAG,"notify start time ="+System.nanoTime());
            method.setAccessible(true);
            method.invoke(object, args);
            MLog.debug(TAG,"notify end time ="+System.nanoTime());
        } catch (IllegalAccessException e) {
            MLog.error(TAG, e);
        } catch (InvocationTargetException e) {
            MLog.error(TAG, e);
        }
    }

}
